﻿using Gasgoo.ShareLibrary.Framework.Global;
using Gasgoo.ShareLibrary.Framework.iDataProvider;
using Gasgoo.ShareLibrary.Framework.iQueueProvider;
using Gasgoo.ShareLibrary.Framework.Options;
using Gasgoo.ShareLibrary.Framework.ServiceDefine;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Orleans;
using Orleans.Configuration;
using Orleans.Hosting;
using Orleans.Providers;
using Orleans.Runtime;
using Orleans.Storage;
using Orleans.Streams;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Gasgoo.ShareLibrary.Framework
{
    public class GasgooClusterHostServer : IClusterService
    {
        readonly ISiloHost iHost;
        readonly IClusterClient iClient;
        readonly IGrainFactory iGrainFactory;
        readonly IServiceProvider ServiceProvider;

        public GasgooClusterHostServer(ISiloHost host)
        {
            this.iHost = host;
            this.ServiceProvider = this.iHost.Services;
            this.iGrainFactory = this.iHost.Services.GetRequiredService<IGrainFactory>();
            this.iClient = host.Services.GetRequiredService<IClusterClient>();
            ServiceManage.RegisterServiceProvider(this.iHost.Services, Constants.SILO_HOST_SERVICE_PROVIDER);
        }

        public async Task StopAsync()
        {
            Console.WriteLine("\nGasgoo> Stoping...");
            await this.iHost.StopAsync();
            await this.iHost.DisposeAsync();
            Console.WriteLine("\nGasgoo> Stoped successfully");
        }


        public T GetService<T>(Guid primaryKey) where T : GasgooServiceWithGuidKey
        {
            return this.iGrainFactory.GetGrain<T>(primaryKey);
        }

        public T GetService<T>(long primaryKey) where T : GasgooServiceWithIntegerKey
        {
            return this.iGrainFactory.GetGrain<T>(primaryKey);
        }

        public T GetService<T>(string primaryKey) where T : GasgooServiceWithStringKey
        {
            return this.iGrainFactory.GetGrain<T>(primaryKey);
        }

        public T GetService<T>(long primaryKey1, string primaryKey2) where T : GasgooServiceWithIntegerCompoundKey
        {
            return this.iGrainFactory.GetGrain<T>(primaryKey1, primaryKey2);
        }

        public T GetService<T>(Guid primaryKey1, string primaryKey2) where T : GasgooServiceWithGuidCompoundKey
        {
            return this.iGrainFactory.GetGrain<T>(primaryKey1, primaryKey2);
        }

        public T GetService<T>() where T : GasgooService
        {
            return this.iGrainFactory.GetGrain<T>(0);
        }

        public IManagement GetManagementer() => new ManagementService(this.iGrainFactory.GetGrain<IManagementGrain>(0));

        public T GetInterface<T>()
        {
#pragma warning disable CS8603 // 可能返回 null 引用。
            return this.ServiceProvider.GetService<T>();
#pragma warning restore CS8603 // 可能返回 null 引用。
        }

        public IQueueProvider GetQueueProvider(string name)
        {
            IStreamProvider stream = this.iClient.GetStreamProvider(name);
            return new DefaultQueueProvider(stream);
        }
    }

    public class GasgooHostBuilder
    {
        readonly SiloHostBuilder builder;
        private AdoNetClusterOptions? clusterOptions;
        private string? ClusterId;
        private const string Invariant = "System.Data.SqlClient";

        public GasgooHostBuilder()
        {
            Console.WriteLine("\nGasgoo> Start Builder GasgooHost...");
            this.builder = new SiloHostBuilder();
            this.Ini();
        }

        void Ini()
        {
            this.builder.AddSimpleMessageStreamProvider(Constants.SMS_PROVIDER);

#if !DEBUG
            this.builder.Configure<ClusterMembershipOptions>(options =>
            {
                options.DeathVoteExpirationTimeout = TimeSpan.FromSeconds(30);
                options.IAmAliveTablePublishTimeout = TimeSpan.FromSeconds(19);
                options.TableRefreshTimeout = TimeSpan.FromSeconds(30);
            });
#endif


            this.builder.AddMemoryGrainStorage(ProviderConstants.DEFAULT_STORAGE_PROVIDER_NAME);
            this.builder.AddMemoryGrainStorage("PubSubStore");
        }

        public GasgooHostBuilder UseAdoNetClustering(AdoNetClusterOptions clusterOptions)
        {
            this.builder.Configure<ClusterOptions>(options =>
            {
                options.ClusterId = clusterOptions.ClusterOptions.ClusterId;
                options.ServiceId = clusterOptions.ClusterOptions.ServiceId;
            });

            this.ClusterId = clusterOptions.ClusterOptions.ClusterId;

            this.clusterOptions = clusterOptions;

            this.ConfigureServices(services =>
            {
                services.AddSingleton(clusterOptions.AdoNetOptions);
                //services.AddSingletonNamedService("KeyValueStorageService", (s, n) => (IGrainStorage)new KeyValueStorageService(s.GetService<IKeyValueStorageProvider>()));
                //services.AddSingletonNamedService("HashStorageService", (s, n) => (IGrainStorage)new HashStorageService(clusterOptions.AdoNetOptions));
                //services.AddSingletonNamedService("SetStorageService", (s, n) => (IGrainStorage)new SetStorageService(clusterOptions.AdoNetOptions));
                //services.AddSingletonNamedService("ZSetStorageService", (s, n) => (IGrainStorage)new ZSetStorageService(clusterOptions.AdoNetOptions));
            });

            this.builder.Configure<SiloMessagingOptions>(options =>
            {
                options.ResponseTimeout = clusterOptions.ResponseTimeout;
                options.ResponseTimeoutWithDebugger = clusterOptions.ResponseTimeout;
            });

            if (clusterOptions.EndpointsOptions.AdvertisedIP != null)
            {
                this.builder.ConfigureEndpoints(
                    clusterOptions.EndpointsOptions.AdvertisedIP,
                    clusterOptions.EndpointsOptions.Port,
                    clusterOptions.EndpointsOptions.GatewayPort, true);
            }
            else
            {
                this.builder.ConfigureEndpoints(clusterOptions.EndpointsOptions.Port, clusterOptions.EndpointsOptions.GatewayPort, listenOnAnyHostAddress: true);
            }


            //builder.UseGasgooStorageAsDefault();

            //builder.AddAdoNetGrainStorage("PubSubStore", options =>
            //{
            //    options.Invariant = Invariant;
            //    options.UseJsonFormat = false;
            //    options.UseXmlFormat = false;
            //    options.ConnectionString = clusterOptions.AdoNetOptions.GetConnection();
            //});

            this.builder.UseAdoNetClustering(options =>
            {
                options.Invariant = Invariant;
                options.ConnectionString = clusterOptions.AdoNetOptions.GetConnection();
            });

            this.builder.UseAdoNetReminderService(options =>
            {
                options.Invariant = Invariant;
                options.ConnectionString = clusterOptions.AdoNetOptions.GetConnection();
            });

            return this;
        }
        //public GasgooHostBuilder UseGasgooStorageAsDefault<T>(T factory) where T : IStorageFactory
        //{

        //    this.UseGasgooStorage(ProviderConstants.DEFAULT_STORAGE_PROVIDER_NAME, factory);
        //    return this;
        //}

        public GasgooHostBuilder UseGasgooStorage<TProvider>(string name, Action<string, IServiceCollection>? configureDelegate = null) where TProvider : class, IDataStorageProvider
        {
            if (configureDelegate != null)
                builder.ConfigureServices(s => configureDelegate?.Invoke(name, s));

            builder.ConfigureServices(services =>
            {
                services.TryAddSingleton(sp => sp.GetServiceByName<IGrainStorage>(ProviderConstants.DEFAULT_STORAGE_PROVIDER_NAME));
                services.AddTransientNamedService<IDataStorageProvider>(name, GasgooUtils.Create<TProvider>);
                services.AddSingletonNamedService(name, new GasgooDataStorageFactory().Create);
                services.AddSingletonNamedService(name, (s, n) =>
                {
                    if (n == ProviderConstants.DEFAULT_STORAGE_PROVIDER_NAME)
                    {
                        return default;
                    }
                    return (ILifecycleParticipant<ISiloLifecycle>)s.GetRequiredServiceByName<IGrainStorage>(n);
                });
            });

            return this;
        }

        //public GasgooHostBuilder UseGasgooStorage<T>(string name, T factory) where T : IStorageFactory
        //{

        //    builder.ConfigureServices(services =>
        //    {
        //        services.TryAddSingleton(sp => sp.GetServiceByName<IGrainStorage>(ProviderConstants.DEFAULT_STORAGE_PROVIDER_NAME));
        //        services.AddSingletonNamedService(name, factory.Create);
        //        services.AddSingletonNamedService(name, (s, n) =>
        //        {
        //            if (n == ProviderConstants.DEFAULT_STORAGE_PROVIDER_NAME)
        //            {
        //                return default;
        //            }
        //            return (ILifecycleParticipant<ISiloLifecycle>)s.GetRequiredServiceByName<IGrainStorage>(n);
        //        });
        //    });

        //    return this;
        //}



        /// <summary>
        /// 启用流（队列）服务。
        /// 消息缓存用于，用户消费失败后进行重试；或者消费者从指定Offset开始消费。
        /// 如使用 UseiCenterClustering 集群请手动注册 AdoNetOptions Service， 否则报错
        /// </summary>
        /// <param name="streamName">流名称</param>
        /// <param name="numOfQueue">流队列数量</param>
        /// <param name="dataMaxAgeInCache">消息在内存中的最大缓存时间</param>
        /// <returns></returns>
        public GasgooHostBuilder UseStreamProvider<TProvider>(string streamName, Action<IGasgooStreamConfigurator>? configure = null)
            where TProvider : class, IQueueStorageProvider
        {

            builder.ConfigureServices(services =>
            {
                services.AddTransientNamedService<IQueueStorageProvider>(streamName, GasgooUtils.Create<TProvider>);
            });

            var memoryStreamConfiguretor = new GasgooServiceStreamConfigurator<AdapterFactory>(
                streamName, configureDelegate => builder.ConfigureServices(configureDelegate));

            configure?.Invoke(memoryStreamConfiguretor);

            //this.builder.UseStreams(streamName, options =>
            //{
            //    options.ConfigurePartitioning(numOfQueue);
            //    options.ConfigureCacheEviction(op => op.Configure(s => s.DataMaxAgeInCache = dataMaxAgeInCache));
            //});

            return this;
        }

        /// <summary>
        /// 配置日志
        /// </summary>
        /// <returns></returns>
        public GasgooHostBuilder ConfigureLogging(Action<ILoggingBuilder> configureLogging)
        {
            this.builder.ConfigureLogging(configureLogging);
            return this;
        }

        /// <summary>
        /// 配置Service
        /// </summary>
        /// <returns></returns>
        public GasgooHostBuilder ConfigureServices(Action<IServiceCollection> configureDelegate)
        {
            this.builder.ConfigureServices(configureDelegate);
            return this;
        }

        public GasgooHostBuilder AddSingleton<TService, TImplementation>()
            where TService : class
            where TImplementation : class, TService
        {
            this.ConfigureServices(services => services.AddSingleton<TService, TImplementation>());
            return this;
        }

        /// <summary>
        /// 服务启动时执行
        /// </summary>
        /// <typeparam name="TStartup"></typeparam>
        /// <returns></returns>
        public GasgooHostBuilder AddStartupTask(Func<IServiceProvider, CancellationToken, Task> startupTask)
        {
            this.builder.AddStartupTask(startupTask);
            return this;
        }

        /// <summary>
        /// 生成并且启动服务
        /// </summary>
        /// <returns></returns>
        public async Task<GasgooClusterHostServer> BuildAndStartAsync()
        {
            if (this.clusterOptions != null)
            {
                await this.clusterOptions.CheckDataStructuresAsync();
            }
            else
            {
                // 未配置则启用本地集群模式
                this.builder.UseLocalhostClustering();
            }

            this.AddFromAppDomain();

            var host = this.builder.Build();

            await host.StartAsync();

            var server = new GasgooClusterHostServer(host);

            await Task.Delay(100);

            new ClusterHostClient(host.Services.GetRequiredService<IClusterClient>());

            Console.WriteLine("\nGasgoo> Started successfully.");

            return server;
        }


        /// <summary>
        /// 加载当前域所有Gasgoo Service
        /// </summary>
        /// <returns></returns>
        private GasgooHostBuilder AddFromAppDomain()
        {
            this.builder.ConfigureApplicationParts(parts => parts.AddFromApplicationBaseDirectory().WithCodeGeneration().WithReferences());
            return this;
        }


        public GasgooHostBuilder AddFromAppDomain(params Type[] types)
        {
            this.builder.ConfigureApplicationParts(parts =>
            {
                foreach (var item in types)
                {
                    parts.AddApplicationPart(item.Assembly).WithReferences();
                }
            });
            return this;
        }


        void config()
        {
            //this.builder.Configure<ClusterMembershipOptions>(options => {
            //    // 成员资格表中死亡投票的过期时间（秒）。
            //    options.DeathVoteExpirationTimeout = TimeSpan.FromSeconds(60 * 2);
            //    // 失效筒仓清理期
            //    options.DefunctSiloCleanupPeriod = TimeSpan.FromHours(7);
            //    // 失效筒仓的成员资格条目有资格删除的时间段。 只有在奥尔良才有效。配置ClusterMembership选项。已失效的IloCleanUpperIOD不为null。
            //    options.DefunctSiloExpiration = TimeSpan.FromDays(7);
            //    // 是否通过其他思洛存储器间接启用探测思洛存储器。
            //    options.EnableIndirectProbes = true;
            //    // 定期在成员资格表中写入此思洛存储器处于活动状态的秒数。仅用于诊断
            //    options.IAmAliveTablePublishTimeout = TimeSpan.FromSeconds(5 * 60);
            //    // 记录局部健康状况恶化状态的自检间隔时间。
            //    options.LocalHealthDegradationMonitoringPeriod = TimeSpan.FromSeconds(10);
            //    // 放弃前尝试加入思洛存储器群集的秒数。
            //    options.MaxJoinAttemptTime = TimeSpan.FromSeconds(5 * 60);
            //    // 来自思洛存储器的丢失的“我还活着”心跳消息的数量或导致怀疑此思洛存储器已死亡的未回复探测的数量。
            //    options.NumMissedProbesLimit = 3;
            //    // 从导致记录警告的思洛存储器在表中错过的“我还活着”更新的次数。不会影响活动性协议。
            //    options.NumMissedTableIAmAliveLimit = 2;
            //    // 每个思洛存储器探测活动性的思洛存储器数量。
            //    options.NumProbedSilos = 3;
            //    // 宣布某个思洛存储器已死亡所需的未过期票数（最多应为NumMissedProbesLimit）
            //    options.NumVotesForDeathDeclaration = 2;
            //    // 周期性地探测其他思洛存储器的活动性或该思洛存储器发送关于自身的“我还活着”心跳消息的秒数。
            //    options.ProbeTimeout = TimeSpan.FromSeconds(5);
            //    // 定期从成员资格表获取更新的秒数。
            //    options.TableRefreshTimeout = TimeSpan.FromSeconds(60);
            //    // 是否使用八卦优化来加速传播活跃度信息。
            //    options.UseLivenessGossip = true;
            //    // 加入集群的新思洛存储器是否必须验证与所有其他活动思洛存储器的初始连接。
            //    options.ValidateInitialConnectivity = true;
            //});
        }
    }
}
